package utils

import (
	"errors"
	"fmt"
	"go-pan/internal/config"
	"time"

	jwt "github.com/golang-jwt/jwt/v5"
)

// ErrDatabaseError 数据库错误类型，用于区分数据库错误和认证失败
type ErrDatabaseError struct {
	Err error
}

func (e *ErrDatabaseError) Error() string {
	return fmt.Sprintf("database error: %v", e.Err)
}

func (e *ErrDatabaseError) Unwrap() error {
	return e.Err
}

// IsDatabaseError 检查错误是否是数据库错误
func IsDatabaseError(err error) bool {
	var dbErr *ErrDatabaseError
	return errors.As(err, &dbErr)
}

var jwtSecret []byte

// InitJWT 初始化JWT配置（从config读取）
func InitJWT() {
	cfg := config.GetConfig()
	jwtSecret = []byte(cfg.JWT.Secret)
}

// GetJWTSecret 获取JWT密钥
func GetJWTSecret() []byte {
	if jwtSecret == nil {
		InitJWT()
	}
	return jwtSecret
}

type Claims struct {
	UserID   int64  `json:"user_id"`
	Username string `json:"username"`
	jwt.RegisteredClaims
}

// 生成token，有效期从配置读取
func GenerateToken(userID int64, username string) (string, error) {
	cfg := config.GetConfig()
	expiration := time.Duration(cfg.JWT.Expiration) * time.Hour

	claims := Claims{
		UserID:   userID,
		Username: username,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(expiration)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "go-pan",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(GetJWTSecret())
}

// 解析token（不检查黑名单，用于获取过期时间等）
func ParseToken(tokenStr string) (*Claims, error) {
	token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return GetJWTSecret(), nil
	})
	if err != nil {
		return nil, err
	}
	if claims, ok := token.Claims.(*Claims); ok && token.Valid {
		return claims, nil
	}
	return nil, jwt.ErrSignatureInvalid
}

// ValidateAccessToken 验证access token（包括黑名单检查）
// 返回 *ErrDatabaseError 表示数据库错误（应返回500），其他错误表示认证失败（应返回401）
func ValidateAccessToken(tokenStr string) (*Claims, error) {
	// 1. 先检查黑名单
	blacklisted, err := IsTokenBlacklisted(tokenStr)
	if err != nil {
		// 数据库错误，返回特殊错误类型以便中间件区分
		return nil, &ErrDatabaseError{Err: fmt.Errorf("failed to check token blacklist: %w", err)}
	}
	if blacklisted {
		return nil, errors.New("token has been revoked")
	}

	// 2. 验证JWT签名和过期时间
	return ParseToken(tokenStr)
}
